/*******************************************************************************
 * Copyright (c) 2009, 2013 EclipseSource and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    EclipseSource - initial API and implementation
 ******************************************************************************/
package it.rap.applications.filters.core;

import static it.rap.applications.filters.utils.ImageUtil.getImage;
import it.rap.application.logic.mappers.FiltersLogicNew;
import it.rap.applications.filters.utils.ImageUtil;
import it.rap.applications.filters.widgets.FilterTreeDetailWidget;
import it.rap.applications.filters.widgets.RoleTreeDetailWidget;
import it.rap.applications.persistence.entities.filters.FilterTree;
import it.rap.applications.persistence.entities.filters.RoleTree;
import it.rap.applications.persistence.entities.filters.TreeObject;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerEditor;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;

import it.rap.applications.container.ContainerUtil;
import it.rap.applications.container.IContainerPage;
import it.rap.applications.contributions.manager.utils.SubMenuAction;

import org.eclipse.rap.rwt.SingletonUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Label;

public class CorePage implements IContainerPage {

	private static final int EDITOR_ACTIVATE = ColumnViewerEditor.TABBING_HORIZONTAL
			| ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR
			| ColumnViewerEditor.TABBING_VERTICAL
			| ColumnViewerEditor.KEYBOARD_ACTIVATION;

	private final static int MODERN_STYLE = 0;
	private final static int TABLE_STYLE = 1;
	private final static int COMPOSITE_COLUMN = 1;
	
	private TreeViewer currentViewer;
	// private TreeViewer simpleTree;
	private TreeViewer complexTree;
	private Composite footerComp;
	private Composite parent;
	
	private Boolean topLeftDetailVisible = false;
	private Composite detail; 
	private FilterTreeDetailWidget fWidget;
	private RoleTreeDetailWidget rWidget;
	
	private static FiltersLogicNew fLogic = new FiltersLogicNew();
	
	private static CheckboxTreeViewer checkboxTreeViewer;
	private static List<FilterTree> filterList;
	private static List<Object> elementsToCheck = new ArrayList<Object>();
	private static List<Object> elementsToRemove = new ArrayList<Object>();
	
	private List<SubMenuAction> actions = new ArrayList<SubMenuAction>();

	/**
	 * @wbp.parser.entryPoint
	 */
	public void wBuilderEntriPoint(Composite parent) {
		Composite contentComp = new Composite(parent, SWT.NONE);
		this.createControl(contentComp);
	}

	// ///////////////
	// create widgets
	// //////////////
	public void createControl(Composite _parent) {		
		parent = _parent;
		parent.setLayout(new GridLayout(COMPOSITE_COLUMN, false));
		//parent.setLayout( ContainerUtil.createMainLayout( 2 ) );
		createHeader(parent);
		//createTopLeft( parent );
		createTopRight(parent);
		//createTopLeft( parent );
		//createFooter(parent);
		//setFocus();
	}
	
	private void createHeader( Composite parent ) {
		Composite composite = new Composite(parent, SWT.NONE);
		composite.setLayout(new GridLayout(1, false));
		composite.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, false, 0, 0));
	    ToolBar toolBar = new ToolBar(composite,  SWT.NONE  );
	    ToolItem dropDown = new ToolItem(toolBar, SWT.DROP_DOWN);
	    dropDown.setText("Actions");
	    dropDown.setImage( getImage( composite.getDisplay(), "no-flag.png" ) );
	    dropDown.setToolTipText("Select for more actions...");
	    Menu menu = new Menu( dropDown.getParent().getShell(), SWT.POP_UP );
	    dropDown.addListener( SWT.Selection, new DropDownSelectionListener( menu ) );
	    SubMenuItemSelectionListener subMenuListener = new SubMenuItemSelectionListener( dropDown );
		  
		initSubMenuAction();
		for( SubMenuAction action: actions  ){
		    MenuItem item = new MenuItem( menu, SWT.PUSH );
		    item.setText( action.getName() );
		    item.setImage( action.getImage() );
		    item.setData( action );
		    item.addListener( SWT.Selection, subMenuListener );
		}
	}

	private void createTopLeft( Composite parent ) { 
		detail = new Composite(parent, SWT.NONE);
		detail.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1));
		detail.setLayout(new GridLayout(1, false));
		
		fWidget = new FilterTreeDetailWidget( detail );
		rWidget = new RoleTreeDetailWidget( detail );
		topLeftDetailVisible = true;
	}
	
	private void createTopRight(Composite parent) {
		Composite composite = new Composite(parent, SWT.NONE);	
		composite.setLayoutData(ContainerUtil.createFillData());
		composite.setLayout(ContainerUtil.createFillLayout(true));
		complexTree = createComplexTree(composite);
	}

	
	private void initSubMenuAction(){
		actions.add(
		new SubMenuAction("New item", getImage( parent.getDisplay(), "add_obj.png" ) ){
				@Override
				public void handleAction() {
					NewButtonSelectionHandler s = new NewButtonSelectionHandler();
					s.handleAction();
				}
		    }
		);
		actions.add( 
			new SubMenuAction("Remove items",  getImage( parent.getDisplay(), "delete_obj.png" ) ){
				@Override
				public void handleAction() {
					RemoveButtonSelectionHandler s = new RemoveButtonSelectionHandler();
					s.handleAction();
				}
		    }		
		);
		actions.add(
			   new SubMenuAction("Save", getImage( parent.getDisplay(), "save_obj.png" ) ){
					@Override
					public void handleAction() {
						SaveButtonSelectionHandler s = new SaveButtonSelectionHandler();
						s.handleAction();
					}
			    }
		);
	}

	private void createFooter(Composite parent) {
		footerComp = new Composite(parent, SWT.NONE);
		footerComp.setLayout(ContainerUtil.createRowLayout(SWT.HORIZONTAL, false));
		createControlButtons(footerComp);
	}

	/*
	 * private void setFocus() { Tree tree = simpleTree.getTree();
	 * tree.forceFocus(); tree.select( tree.getItem( 0 ) ); }
	 */

	private void createControlButtons(Composite parent) {
		footerComp.setLayout(new GridLayout(3, false));
		Button newButton = new Button(parent, SWT.PUSH);
		newButton.setText("New Item");
		newButton.addSelectionListener(new NewButtonSelectionHandler());
		Button removeButton = new Button(parent, SWT.PUSH);
		removeButton.setText("Remove Item(s)");
		removeButton.addSelectionListener(new RemoveButtonSelectionHandler());
		Button saveButton = new Button(parent, SWT.PUSH);
		saveButton.setText("Save Item(s)");
		saveButton.addSelectionListener(new SaveButtonSelectionHandler());
	}

	/*
	 * private TreeViewer createSimpleTree( Composite parent ) { Tree tree = new
	 * Tree( parent, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION ); TreeViewer
	 * result = new TreeViewer( tree ); result.setContentProvider( new
	 * TreeContentProvider() ); TreeLabelProvider labelProvider = new
	 * TreeLabelProvider( parent.getDisplay(), MODERN_STYLE );
	 * result.setLabelProvider( labelProvider ); result.setInput( createModel()
	 * ); result.expandAll(); tree.addFocusListener( new
	 * TreeFocusGainedHandler() ); addDNDSupport( result ); addCellEditor(
	 * result ); addToolTipSupport( result ); return result; }
	 */

	private TreeViewer createComplexTree(Composite parent) {
		int style = SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION | SWT.CHECK;
		Tree tree = new Tree(parent, style);
		createColumn(tree, "Name", SWT.LEFT, 250);
		createColumn(tree, "Regexp", SWT.CENTER, 250);
		createColumn(tree, "Description", SWT.CENTER, 250);
		tree.setLinesVisible(true);
		tree.setHeaderVisible(true);
		checkboxTreeViewer = new CheckboxTreeViewer(tree);
		checkboxTreeViewer.addCheckStateListener(new TreeCheckStateListener(checkboxTreeViewer));
		checkboxTreeViewer.setContentProvider(new TreeContentProvider());
		TreeLabelProvider labelProvider = new TreeLabelProvider(parent.getDisplay(), TABLE_STYLE);
		checkboxTreeViewer.setLabelProvider(labelProvider);
		checkboxTreeViewer.setInput(createModel());
		checkboxTreeViewer.setCheckedElements( elementsToCheck.toArray() );
		checkboxTreeViewer.addSelectionChangedListener( new ISelectionChangedListener() {
		      public void selectionChanged( SelectionChangedEvent event ) {
		          updateDetail();
		        }
		      } );
		checkboxTreeViewer.expandAll();
		tree.addFocusListener(new TreeFocusGainedHandler());
		addDNDSupport(checkboxTreeViewer);
		addCellEditor(checkboxTreeViewer);
		addToolTipSupport(checkboxTreeViewer);
		return checkboxTreeViewer;
	}

	private static TreeColumn createColumn(Tree parent, String name, int style, int width) {
		TreeColumn result = new TreeColumn(parent, style);
		result.setText(name);
		result.setWidth(width);
		result.setMoveable(true);
		result.setResizable(true);
		return result;
	}

	private void updateDetail() {
		if ( !topLeftDetailVisible ) return;
		StructuredSelection sel = ( StructuredSelection )checkboxTreeViewer.getSelection();
		TreeObject selectedObject = ( TreeObject )sel.getFirstElement();
		
	    if ( selectedObject instanceof FilterTree ){
	    	FilterTree filterSelected = (FilterTree) selectedObject;
	    	
	    	fWidget.getNameFilterLabelValue().setText( filterSelected.getName() );	    	
	    	fWidget.getDescFilterLabelValue().setText( filterSelected.getDescription() );
	    	
	    } else if ( selectedObject instanceof RoleTree ){
	    	RoleTree roleSelected = (RoleTree) selectedObject;
	    	rWidget.getNameRoleLabelValue().setText( roleSelected.getName() );
	    	rWidget.getDescRoleLabelValue().setText( roleSelected.getDescription() );
	    	rWidget.getRegExpRoleLabelValue().setText( roleSelected.getRegexpPattern() );
	    		    	
	    	FilterTree filterSelected = roleSelected.getFilterTree();
	    	if ( filterSelected != null ){
	    		fWidget.getNameFilterLabelValue().setText( filterSelected.getName() );	    	
	    		fWidget.getDescFilterLabelValue().setText( filterSelected.getDescription() );
	    	} else {
	    		filterSelected = ((RoleTree)roleSelected.getParent()).getFilterTree();
	    		if ( filterSelected != null ){
		    		fWidget.getNameFilterLabelValue().setText( filterSelected.getName() );	    	
		    		fWidget.getDescFilterLabelValue().setText( filterSelected.getDescription() );
		    	}
	    	}
	    }
	    detail.setVisible( true );	    
	}
	
	// ////////////
	// Drag & Drop

	private static void addDNDSupport(TreeViewer viewer) {
		Transfer[] types = new Transfer[] { TreeObjectTransfer.getInstance() };
		TreeDragListener dragListener = new TreeDragListener(viewer);
		viewer.addDragSupport(DND.DROP_MOVE, types, dragListener);
		TreeDropListener dropListener = new TreeDropListener(viewer);
		viewer.addDropSupport(DND.DROP_MOVE, types, dropListener);
	}

	// ////////////
	// Cell-editor

	private static void addCellEditor(TreeViewer viewer) {
		CellEditor[] editors = new CellEditor[] {
				new TextCellEditor(viewer.getTree()),
				new TextCellEditor(viewer.getTree()),
				new TextCellEditor(viewer.getTree()) };
		viewer.setCellEditors(editors);
		viewer.setCellModifier(new CellModifier(viewer));
		String[] columnProperties = new String[] { "Name", "Regexp", "Description" };
		viewer.setColumnProperties(columnProperties);
		ColumnViewerEditorActivationStrategy activationStrategy = new CellEditorActivationStrategy(viewer);
		TreeViewerEditor.create(viewer, activationStrategy, EDITOR_ACTIVATE);
	}

	private static void addToolTipSupport(TreeViewer viewer) {
		ColumnViewerToolTipSupport.enableFor(viewer);
	}

	private static TreeObject createModel() {
		TreeObject result = new TreeObject();
		elementsToCheck = new ArrayList<Object>();
		elementsToRemove = new ArrayList<Object>();

		filterList = fLogic.selectAll();

		if ( filterList.size() > 0) {
			for (FilterTree f : filterList ) {
				f.removeAllChild();
				if (f.getRoleTreeList() != null && f.getRoleTreeList().size() > 0) {
					for (RoleTree r : f.getRoleTreeList()) {						
						f.addChild( r );
						if ( r.isChecked() ){
							elementsToCheck.add( r );
						}
						r.removeAllChild();
						if (r.getSubRoleTreeList() != null && r.getSubRoleTreeList().size() > 0) {
							for( RoleTree sr: r.getSubRoleTreeList() ){
								r.addChild( sr );
								if ( sr.isChecked() ){
									elementsToCheck.add( sr );
								}
							}
						}
					}
				}
				result.addChild(f);
				if ( f.isChecked() ){
					elementsToCheck.add( f );
				}
			}
		} else {
				//Inserisco dei filtri di esempio
				FilterTree filter = new FilterTree( "Filter2", "Descrizione filtro1" );
			    result.addChild( filter );
			    RoleTree r11 =  new RoleTree( "Role1", "e=r", "A" );
			    r11.setFilterTree( filter );
			    filter.addChild(r11);
			    filter.getRoleTreeList().add(r11);
			    RoleTree r12 = new RoleTree( "Role2", "JS * kil", "B" );
			    r12.setFilterTree( filter );
			    filter.addChild( r12 );
			    filter.getRoleTreeList().add(r12);
			    
			    FilterTree filter2 = new FilterTree( "Filter3", "Descrizione filtro2" );
			    result.addChild( filter2 );
			    RoleTree r21 =  new RoleTree( "Role3", "e=r", "A" );
			    r21.setFilterTree( filter2 );
			    filter2.addChild( r21 );
			    filter2.getRoleTreeList().add(r21);
			    
			    RoleTree r22 = new RoleTree( "Role4", "JS * kil", "B" );
			    r22.setFilterTree( filter2 );
			    filter2.addChild( r22 );
			    filter2.getRoleTreeList().add(r22);
		}
		return result;
	}
	
	public class SubMenuItemSelectionListener implements Listener {

		private static final long serialVersionUID = -3928858460335365662L;
		private final ToolItem dropDown;

	    public SubMenuItemSelectionListener( ToolItem dropDown ) {
	      this.dropDown = dropDown;
	    }

	    public void handleEvent( Event event ) {
	      MenuItem item = ( MenuItem )event.widget;
	      SubMenuAction action = ( SubMenuAction )item.getData();
	      
	      if ( action.getImage() != null ){
	    	  dropDown.setImage( item.getImage() );
	    	  dropDown.setToolTipText( "Your last action was: " + action.getName() );
	      }
	      dropDown.setData( action );
	      
	      executeAction( action );
	    }

		private void executeAction(SubMenuAction action) {
			action.handleAction();
		}


	}

	public class DropDownSelectionListener implements Listener {

		private static final long serialVersionUID = 3413309950050125164L;
		private final Menu menu;

	    public DropDownSelectionListener( Menu menu ) {
	      this.menu = menu;
	    }

	    public void handleEvent( Event event ) {
	      ToolItem dropDown = ( ToolItem )event.widget;
	      if( event.detail == SWT.ARROW ) {
	        Point point = dropDown.getParent().toDisplay( event.x, event.y );
	        menu.setLocation( point );
	        menu.setVisible( true );
	      } else {
	    	  final SubMenuAction dropDownAction = (SubMenuAction) dropDown.getData();
	    	  if ( dropDownAction == null ) return;
	    	  MessageDialog confirm = new MessageDialog(parent.getShell(), "Recall action?", null, "Recall action "+dropDownAction.getName()+"?", MessageDialog.CONFIRM, new String[]{ "OK", "Annulla" }, 1){
				private static final long serialVersionUID = 4972001426579301330L;
				@Override
	    	      protected int getShellStyle() {
	    	        return super.getShellStyle() | SWT.SHEET;
	    	      }
	    	      @Override
	    	      public boolean close() {
	    	        boolean result = super.close();
	    	        int code = getReturnCode();
	    	        if ( code == 0 ){
	    	        	dropDownAction.handleAction();
	    	        }
	    	        return result;
	    	      }
	    	    };
	    	    confirm.open();
	    	  }
	      }
	    }

	private final class TreeFocusGainedHandler extends FocusAdapter {
		private static final long serialVersionUID = -5203081395268643571L;

		@Override
		public void focusGained(FocusEvent event) {
			currentViewer = null;
			Tree currentTree = (Tree) event.widget;
			/*
			 * if( simpleTree.getTree() == currentTree ) { currentViewer =
			 * simpleTree; } else
			 */
			if (complexTree.getTree() == currentTree) {
				currentViewer = complexTree;
			}
		}
	}

	
	private static class TreeCheckStateListener implements ICheckStateListener {
		private final CheckboxTreeViewer viewer;

		TreeCheckStateListener(CheckboxTreeViewer viewer) {
			this.viewer = viewer;
		}

		public void checkStateChanged(CheckStateChangedEvent event) {
			TreeObject treeObject = (TreeObject) event.getElement();
			boolean checked = event.getChecked();
			updateChildren(checked, treeObject);
			updateParent(treeObject);
			updateCheckFilter( checked, treeObject );
		}

		private void updateParent(TreeObject treeObject) {
			TreeObject parent = treeObject.getParent();
			if (parent != null) {
				boolean parentChecked = true;
				if ( parent instanceof FilterTree  ){
					TreeObject[] children = parent.getChildren();
					boolean isOneChecked = false;
					for (int i = 0; parentChecked && i < children.length; i++) {
						TreeObject child = children[i];
						if ( viewer.getChecked(child) ) {
							isOneChecked = true;
						}
					}
					if ( !isOneChecked ){
						parentChecked = false;
					}
				}
				viewer.setChecked(parent, parentChecked);
				updateCheckFilter( parentChecked, parent );
				updateParent(parent);
			}
		}

		private void updateChildren(boolean checked, TreeObject parent) {
			TreeObject[] children = parent.getChildren();
			if ( checked && parent instanceof RoleTree ) return;
			if ( children == null ) return;
			for (int i = 0; i < children.length; i++) {
				TreeObject treeObject = children[i];
				updateCheckFilter( checked, treeObject );
				viewer.setChecked(treeObject, checked);
				if (treeObject.hasChildren()) {
					updateChildren(checked, treeObject);
				}
			}
		}
		
		private void updateCheckFilter(boolean checked, TreeObject treeObject) {
			if ( treeObject instanceof FilterTree ){
				FilterTree filterTree = (FilterTree)treeObject;
				if ( filterTree.isChecked() != checked ){
					filterTree.setChecked( checked );
					filterTree.setModified(true);
				}
				
			} else  if ( treeObject instanceof RoleTree ){
				RoleTree roleTree = (RoleTree)treeObject;
				if ( roleTree.isChecked() != checked ){
					roleTree.setChecked( checked );
					roleTree.setModified( true );					
				}
			}
		}
	}

	private final class RemoveButtonSelectionHandler extends SelectionAdapter {
		private static final long serialVersionUID = -7820856590707066160L;

		@Override
		public void widgetSelected(SelectionEvent event) {
			handleAction();
		}

		public void handleAction() {
			if (currentViewer != null && !currentViewer.getSelection().isEmpty()) {
				ITreeSelection sel = (ITreeSelection) currentViewer.getSelection();
				Iterator<?> iterator = sel.iterator();
				TreeObject parent = null;
				while (iterator.hasNext()) {
					TreeObject obj = (TreeObject) iterator.next();
					parent = obj.getParent();
					if (parent != null) {
						parent.removeChild(obj);
						elementsToRemove.add(obj);
					}
				}
				if (parent != null) {
					TreeObject newSel = null;
					if (parent.getParent() == null) {
						TreeObject[] children = parent.getChildren();
						if (children.length > 0) {
							newSel = children[0];
						}
					} else {
						newSel = parent;
					}
					if (newSel != null) {
						currentViewer.setSelection(new StructuredSelection(newSel));
					}
				}
				currentViewer.refresh();
				currentViewer.getTree().forceFocus();
			}
		}
	}

	private final class NewButtonSelectionHandler extends SelectionAdapter {
		/**
		 * 
		 */
		private static final long serialVersionUID = 986275026424565606L;

		@Override
		public void widgetSelected(SelectionEvent event) {
			handleAction();
		}
		
		public void handleAction(){
			if (currentViewer != null && !currentViewer.getSelection().isEmpty()) {
				//Ho selezionaato una riga
				ITreeSelection sel = (ITreeSelection) currentViewer.getSelection();
				if ( sel.getFirstElement() instanceof FilterTree ){
					//Ho selezionato una riga di tipo Filtro
					//Creo una Role
					FilterTree filterTree = (FilterTree) sel.getFirstElement();
					createRole( filterTree );
				} else if ( sel.getFirstElement() instanceof RoleTree ) {
					//Ho selezionato una riga di tipo Role
					// => creo una subRole
					RoleTree roleTree = (RoleTree) sel.getFirstElement();
					if ( roleTree.getParent() instanceof FilterTree ){
						createSubRole( roleTree );
					}
				}
			} else {
				//Non ho selezionato una riga
				// => creo un filtro
				TreeObject o = (TreeObject) currentViewer.getInput();
				createFilter(o);
			}			
		}
		
		private void createSubRole(RoleTree roleTree) {
			RoleTree newRole = new RoleTree("New sub role", "*", "*");
			newRole.setSubRoleTree( roleTree );
			roleTree.getSubRoleTreeList().add( newRole );
			roleTree.addChild(newRole);
			currentViewer.expandToLevel(roleTree, 1);
			currentViewer.refresh();
			currentViewer.setSelection(new StructuredSelection(newRole));
			currentViewer.getTree().forceFocus();
		}

		private void createFilter(TreeObject o){
			FilterTree filterNew = new FilterTree( "New Filter", "Descrizione filtro" );
			o.addChild( filterNew );
			RoleTree newRole =  new RoleTree( "New Role", "*", "*" );
			newRole.setFilterTree( filterNew );
			filterNew.addChild( newRole );
			filterNew.getRoleTreeList().add(newRole);
			currentViewer.expandToLevel(filterNew, 1);
			currentViewer.refresh();
			currentViewer.setSelection(new StructuredSelection(newRole));
			currentViewer.getTree().forceFocus();
		}
		
		private void createRole(FilterTree filterTree){
			RoleTree newRole = new RoleTree("New role ", "", "New descr");
			newRole.setFilterTree( filterTree );
			filterTree.getRoleTreeList().add( newRole );
								
			filterTree.addChild(newRole);
			currentViewer.expandToLevel(filterTree, 1);
			currentViewer.refresh();
			currentViewer.setSelection(new StructuredSelection(newRole));
			currentViewer.getTree().forceFocus();
		}
	}

	private final class SaveButtonSelectionHandler extends SelectionAdapter {
		private static final long serialVersionUID = -3999345730318721921L;

		@Override
		public void widgetSelected(SelectionEvent event) {
			handleAction();
		}

		public void handleAction() {
			Tree tree = ((TreeViewer) complexTree).getTree();
			TreeItem[] items = tree.getItems();

			saveList(items, null);
			removeElements();
		}

		private void removeElements() {
			if ( elementsToRemove!=null && elementsToRemove.size() > 0 ){
				for( Object o: elementsToRemove ){
					if ( o instanceof RoleTree ){
						fLogic.remove( (RoleTree)o );	
					} else if ( o instanceof FilterTree ) {
						fLogic.remove( (FilterTree)o );	
					}
				}
			}
			elementsToRemove = null;
		}
		
		private void saveList(TreeItem[] items, FilterTree filter) {
			if (items != null && items.length > 0) {
				for (TreeItem i : items) {
					Object o = i.getData();
					if (o instanceof FilterTree) {
						FilterTree f = (FilterTree)o;
						if ( f.getId() == null || f.isModified() ){
							fLogic.persist( f );
						}
						f.setModified(false);
					} else if (o instanceof RoleTree) {
						RoleTree r = (RoleTree)o;
						if ( r.getId() == null || r.isModified() ){
							fLogic.persist( r );
						}
						r.setModified(false);
					}
					saveList(i.getItems(), filter);
				}
			}
			return;
		}
	}



	// ////////////
	// Drag & Drop

	private static class TreeDragListener extends DragSourceAdapter {
		/**
		 * 
		 */
		private static final long serialVersionUID = -98688770354230212L;
		private final TreeViewer viewer;
		private Object dragData;

		TreeDragListener(TreeViewer viewer) {
			this.viewer = viewer;
		}

		@Override
		public void dragStart(DragSourceEvent event) {
			dragData = getTreeObject(event.x, event.y);
		}

		@Override
		public void dragSetData(DragSourceEvent event) {
			event.data = dragData;
		}

		@Override
		public void dragFinished(DragSourceEvent event) {
			viewer.refresh();
		}

		private TreeObject getTreeObject(int x, int y) {
			TreeObject result = null;
			ViewerCell cell = viewer.getCell(new Point(x, y));
			if (cell != null) {
				result = (TreeObject) cell.getElement();
			}
			return result;
		}
	}

	private static class TreeDropListener extends DropTargetAdapter {
		/**
		 * 
		 */
		private static final long serialVersionUID = 5192557893695043518L;
		private final TreeViewer viewer;

		public TreeDropListener(TreeViewer viewer) {
			this.viewer = viewer;
		}

		@Override
		public void dragEnter(DropTargetEvent event) {
			event.feedback = DND.FEEDBACK_EXPAND | DND.FEEDBACK_SELECT
					| DND.FEEDBACK_SCROLL;
		}

		@Override
		public void drop(DropTargetEvent event) {
			if (event.data == null || event.item == null) {
				event.detail = DND.DROP_NONE;
			} else {
				TreeObject draggedObject = (TreeObject) event.data;
				TreeObject targetObject = (TreeObject) event.item.getData();
				if (isValidDrop(draggedObject, targetObject)) {
					draggedObject.getParent().removeChild(draggedObject);
					targetObject.addChild(draggedObject);
					viewer.refresh();
				}
			}
		}

		private static boolean isValidDrop(TreeObject draggedObject, TreeObject targetObject) {
			boolean result = false;
			if (draggedObject != null && targetObject != null) {
				result = true;
				TreeObject current = targetObject;
				while (current != null && result) {
					result = current != draggedObject;
					current = current.getParent();
				}
			}
			return result;
		}
	}

	private static class TreeObjectTransfer extends Transfer {
		/**
		 * 
		 */
		private static final long serialVersionUID = -7414005731699869114L;
		private static final String TYPE_NAME = "treeObject";
		private static final int TYPE_ID = registerType(TYPE_NAME);

		private TreeObjectTransfer() {
		}

		public static TreeObjectTransfer getInstance() {
			return SingletonUtil.getSessionInstance(TreeObjectTransfer.class);
		}

		@Override
		protected int[] getTypeIds() {
			return new int[] { TYPE_ID };
		}

		@Override
		protected String[] getTypeNames() {
			return new String[] { TYPE_NAME };
		}

		@Override
		public TransferData[] getSupportedTypes() {
			int[] types = getTypeIds();
			TransferData[] result = new TransferData[types.length];
			for (int i = 0; i < types.length; i++) {
				result[i] = new TransferData();
				result[i].type = types[i];
			}
			return result;
		}

		@Override
		public boolean isSupportedType(TransferData transferData) {
			boolean result = false;
			if (transferData != null) {
				int[] types = getTypeIds();
				for (int i = 0; !result && i < types.length; i++) {
					if (transferData.type == types[i]) {
						result = true;
					}
				}
			}
			return result;
		}

		@Override
		public void javaToNative(Object object, TransferData transferData) {
			transferData.data = object;
		}

		@Override
		public Object nativeToJava(TransferData transferData) {
			return transferData.data;
		}
	}

	private static final class CellModifier implements ICellModifier {
		private final TreeViewer viewer;

		CellModifier(TreeViewer viewer) {
			this.viewer = viewer;
		}

		public boolean canModify(Object element, String property) {
			return element instanceof RoleTree || property.equals("Name");
		}

		public Object getValue(Object element, String property) {
			String result = "";
			TreeObject treeObject = (TreeObject) element;
			if (property.equals("Name")) {
				result = treeObject.getName();
			} else if (property.equals("Regexp")) {
				RoleTree role = (RoleTree) treeObject;
				result = role.getRegexpPattern();
			} else if (property.equals("Description")) {
				RoleTree role = (RoleTree) treeObject;
				result = role.getDescription();
			} else {
				throw new IllegalArgumentException("Unkown property "+ property);
			}
			return result;
		}

		public void modify(Object element, String property, Object value) {
			TreeObject treeObject;
			if (element instanceof Item) {
				treeObject = (TreeObject) ((Item) element).getData();
			} else {
				treeObject = (TreeObject) element;
			}
			String string = (String) value;
			if (property.equals("Name")) {
				treeObject.setName(string);
				treeObject.setModified( true );
			} else if (property.equals("Regexp")) {
				RoleTree role = (RoleTree) treeObject;
				role.setRegexpPattern(string);
				role.setModified( true );
			} else if (property.equals("Description")) {
				RoleTree role = (RoleTree) treeObject;
				role.setDescription(string);
				role.setModified( true );
			} else {
				throw new IllegalArgumentException("Unkown property " + property);
			}
			viewer.update(treeObject, new String[] { property });
		}
	}

	private static final class CellEditorActivationStrategy extends
			ColumnViewerEditorActivationStrategy {

		/**
		 * 
		 */
		private static final long serialVersionUID = 433147194449837794L;

		CellEditorActivationStrategy(ColumnViewer viewer) {
			super(viewer);
		}

		@Override
		protected boolean isEditorActivationEvent(
				ColumnViewerEditorActivationEvent event) {
			boolean isTraversal = event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL;
			boolean isDoubleClick = event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION;
			boolean isProgrammatic = event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
			return isTraversal || isDoubleClick || isProgrammatic;
		}
	}

	// //////
	// Model
	
	private static class City extends TreeObject {
		private String timezone;		
		private int offset;

		public City(String name, String timezone, int offset) {
			super();
			setName(name);
			setTimeZone(timezone);
			setOffset(offset);
		}

		private void setOffset(int offset) {
			this.offset = offset;
		}

		public void setTimeZone(String tz) {
			timezone = tz;
		}

		private int getOffset() {
			return offset;
		}

		public String getTimeZone() {
			return timezone;
		}
	}
	
	private static final class TreeLabelProvider extends CellLabelProvider {
		/**
		 * 
		 */
		private static final long serialVersionUID = -8898766023726703483L;
		private static final String ICON_GREENDOT = "greendot.gif";
		private static final String ICON_WORLD = "world.gif";
		private static final String ICON_EARTH = "earth-icon.png";

		private static final int COLUMN_TEXT = 0;
		private static final int COLUMN_DESCRIPTION = 2;
		private static final int COLUMN_REGEXPPATTERN = 1;

		private final Device device;
		private final Image continentImage;
		private final Image cityImage;
		private final Font cityFont;
		private final Font continentFont;
		private final Color regexpTextColor;
		private final Color descriptionTextColor;
		private final int style;

		TreeLabelProvider(Device device, int style) {
			this.device = device;
			this.style = style;
			cityFont = createFont("Times New Roman", 13, SWT.NONE);
			continentFont = createFont("Arial", 14, SWT.ITALIC);
			regexpTextColor = new Color(device, 239, 41, 41);
			descriptionTextColor = new Color(device, 252, 175, 62);
			if (style == MODERN_STYLE) {
				continentImage = ImageUtil.getImage(device, ICON_EARTH);
				cityImage = ImageUtil.getImage(device, ICON_GREENDOT);
			} else {
				continentImage = ImageUtil.getImage(device, ICON_WORLD);
				cityImage = ImageUtil.getImage(device, ICON_GREENDOT);
			}
		}

		@Override
		public void update(ViewerCell cell) {
			TreeObject treeObject = (TreeObject) cell.getElement();
			int columnIndex = cell.getColumnIndex();
			switch (columnIndex) {
			case COLUMN_TEXT:
				updateName(cell, treeObject);
				break;
			case COLUMN_REGEXPPATTERN:
				updateRegexpPattern(cell, treeObject);
				break;
			case COLUMN_DESCRIPTION:
				updateDescription(cell, treeObject);
				break;
			}
		}

		@Override
		public String getToolTipText(Object element) {
			String result = "";
			if (element instanceof RoleTree) {
				RoleTree role = (RoleTree) element;
				String roleName = role.getName();
				String roleDescr = role.getDescription();
				result = roleName + " ( " + roleDescr + ")";
			}
			return result;
		}

		private void updateName(ViewerCell cell, TreeObject treeObject) {
			cell.setText(treeObject.getName());
			if (style == MODERN_STYLE) {
				if (treeObject instanceof City) {
					cell.setFont(cityFont);
				} else {
					cell.setFont(continentFont);
				}
			}
			cell.setImage(treeObject instanceof RoleTree ? cityImage
					: continentImage);
		}

		private void updateRegexpPattern(ViewerCell cell, TreeObject treeObject) {
			if (treeObject instanceof RoleTree) {
				RoleTree role = (RoleTree) treeObject;
				cell.setText(role.getRegexpPattern());
				if (style == TABLE_STYLE) {
					cell.setForeground(regexpTextColor);
				}
			}
		}

		private void updateDescription(ViewerCell cell, TreeObject treeObject) {
			if (treeObject instanceof RoleTree) {
				if (style == TABLE_STYLE) {
					cell.setForeground(descriptionTextColor);
				}
				RoleTree role = (RoleTree) treeObject;
				cell.setText(role.getDescription());
			}
		}

		private Font createFont(String name, int size, int style) {
			FontData fontData = new FontData(name, size, style);
			return new Font(device, fontData);
		}
		/*
		 * private static String getUTCOffset( City city ) { String sign =
		 * city.getOffset() >= 0 ? "-" : ""; return "UTC " + sign +
		 * String.valueOf( city.getOffset() ); }
		 */
	}

	private static class TreeContentProvider implements ITreeContentProvider {

		/**
		 * 
		 */
		private static final long serialVersionUID = -8506822432611652179L;

		public Object[] getElements(Object parent) {
			return getChildren(parent);
		}

		public Object getParent(Object child) {
			Object result = null;
			if (child instanceof RoleTree) {
				result = ((RoleTree) child).getParent();
			}
			return result;
		}

		public Object[] getChildren(Object parent) {
			Object[] result = new Object[0];
			if (parent instanceof TreeObject) {
				result = ((TreeObject) parent).getChildren();
			}
			return result;
		}

		public boolean hasChildren(Object parent) {
			boolean result = false;
			if (parent instanceof TreeObject) {
				result = ((TreeObject) parent).hasChildren();
			}
			return result;
		}

		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		}

		public void dispose() {
		}
	}
}
